package com.bnq.logc.upload

import android.util.Log
import com.bnq.logc.LogcManager
import com.bnq.logc.config.LogcConfig
import com.tencent.cos.xml.CosXmlServiceConfig
import com.tencent.cos.xml.CosXmlSimpleService
import com.tencent.cos.xml.exception.CosXmlClientException
import com.tencent.cos.xml.exception.CosXmlServiceException
import com.tencent.cos.xml.listener.CosXmlResultListener
import com.tencent.cos.xml.model.CosXmlRequest
import com.tencent.cos.xml.model.CosXmlResult
import com.tencent.cos.xml.transfer.TransferConfig
import com.tencent.cos.xml.transfer.TransferManager
import com.tencent.qcloud.core.auth.QCloudCredentialProvider
import com.tencent.qcloud.core.auth.ShortTimeCredentialProvider
import com.tencent.qcloud.core.http.OkHttpClientImpl
import com.tencent.qcloud.core.http.QCloudHttpClient
import okhttp3.*
import okio.Buffer
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit

object CosFileClientManager {

    private lateinit var mCredentialProvider: QCloudCredentialProvider
    private lateinit var mTransferManager:TransferManager
    private var mBucket:String = ""
    private lateinit var mConfig:LogcConfig
    //若存在初始化分块上传的 UploadId，则赋值对应的 uploadId 值用于续传；否则，赋值 null
    private var uploadId:String? = null
    private lateinit var mOkHttpClient: OkHttpClient

    init {
        initConfig()
    }

    private fun getOkHttpClient():OkHttpClient{
        var okHttpClient:OkHttpClient? = null//getOkHttpClientFromCos()
        if(okHttpClient == null){
            okHttpClient = OkHttpClient.Builder().apply {
                connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                writeTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                readTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                if(LogcManager.getConfig().mIsDebug){
                    addInterceptor(object :Interceptor{

                        private val TAG = "Logc-Cos"

                        override fun intercept(chain: Interceptor.Chain): Response {
                            val request = chain.request()
                            val key = request.hashCode().toString()
                            var response:Response
                            try{
                                processRequest(key,request)
                                response = chain.proceed(request)
                                response = processResponseBody(key,response)
                            }catch (e: IOException){
                                processError(key,request,e)
                                throw e
                            }
                            return response
                        }

                        fun processError(key:String,request:Request,e: IOException){
                            Log.e(TAG,"Http error url:${request.url()}\nkey:${key}\nheaders:${request.headers()}\nerror:${e.message}")
                        }

                        fun processRequest(key:String,request:Request):Request{
                            return request.body()?.let {
                                val buffer = Buffer()
                                it.writeTo(buffer)
                                val originBodyString = buffer.readUtf8()
                                Log.i(TAG,"Http request url:${request.url()}\nkey:${key}\nheaders:${request.headers()}\nbody:$originBodyString")
                                request.newBuilder().post(RequestBody.create(it.contentType(),originBodyString)).build()
                            }?:request
                        }

                        fun processResponseBody(key:String,response:Response):Response{
                            val body = response.body()
                            if(body != null){
                                val bodyString = body.string()
                                val buildResponse =
                                    response.newBuilder().body(ResponseBody.create(body.contentType(),bodyString)).build()
                                Log.i(TAG,"Http response url:${response.request().url()}\nkey:${key}\nheaders:${response.headers()}\nbody:$bodyString\n")
                                return buildResponse
                            }else{
                                Log.i(TAG,"Http response url:${response.request().url()}\nkey:${key}\nheaders:${response.headers()}\n")
                                return response
                            }
                        }
                    })
                }
            }.build()
        }

        return okHttpClient!!
    }

    private fun getOkHttpClientFromCos():OkHttpClient?{
        try{
            val networkClientMapField = QCloudHttpClient::class.java.getDeclaredField("networkClientMap")
            networkClientMapField.isAccessible = true
            val networkClientMap = networkClientMapField.get(null)
            if(networkClientMap is Map<*,*>){
                val okHttpClientMap = networkClientMap.filterValues {
                    OkHttpClientImpl::class.java.isInstance(it)
                }
                if(okHttpClientMap.isNotEmpty()){
                    okHttpClientMap.forEach {
                        val okHttpClientField = it.value!!.javaClass.getDeclaredField("okHttpClient")
                        okHttpClientField.isAccessible = true
                        return okHttpClientField.get(it.value) as OkHttpClient?
                    }
                }
            }
        }catch (e:Exception){
            e.printStackTrace()
        }
        return null
    }

    private fun initConfig(){
        val config: LogcConfig = LogcManager.getConfig()
        this.mConfig = config
        mCredentialProvider = ShortTimeCredentialProvider(config.mCosSecretId, config.mCosSecretKey, 300)

        val serviceConfig =CosXmlServiceConfig.Builder()
            .setRegion(config.mCosRegion)
            .isHttps(true)
            .builder()

        mBucket = config.mCosBucket

        val cosXmlService = CosXmlSimpleService(config.mContent,serviceConfig,mCredentialProvider)

        val transferConfig = TransferConfig.Builder().build()
        mTransferManager = TransferManager(cosXmlService, transferConfig)

        val okHttpClient = getOkHttpClient()
        mOkHttpClient = okHttpClient
    }

    private fun getCloudName(file: File):String{
        return "${UfileClientManager.mConfig.getCloudParentDir(file)}/${file.name}"
    }

    fun upload(file: File,callback:FileUploadResultCallback<CosXmlResult?,CosXmlClientException?,CosXmlServiceException?>):String{
       val cloudName = getCloudName(file)
       val cosXmlUploadTask = mTransferManager.upload(mBucket,cloudName,file.absolutePath, uploadId)
        /*cosXmlUploadTask.setCosXmlProgressListener { complete, target ->
            println("cosXmlProgressListener:$complete, $target")
        }*/
        cosXmlUploadTask.setCosXmlResultListener(object :CosXmlResultListener{
            override fun onSuccess(request: CosXmlRequest?, result: CosXmlResult?) {
                callback.onSuccess(result)
            }

            override fun onFail(
                request: CosXmlRequest?,
                clientException: CosXmlClientException?,
                serviceException: CosXmlServiceException?
            ) {
                callback.onFail(clientException,serviceException)
            }
        })
        return cloudName
    }

    fun uploadCommon(file: File,cloudPathName:String,callback:FileUploadResultCallback<CosXmlResult?,CosXmlClientException?,CosXmlServiceException?>):String{
        val cosXmlUploadTask = mTransferManager.upload(mBucket,cloudPathName,file.absolutePath, uploadId)
        cosXmlUploadTask.setCosXmlResultListener(object :CosXmlResultListener{
            override fun onSuccess(request: CosXmlRequest?, result: CosXmlResult?) {
                callback.onSuccess(result)
            }

            override fun onFail(
                request: CosXmlRequest?,
                clientException: CosXmlClientException?,
                serviceException: CosXmlServiceException?
            ) {
                callback.onFail(clientException,serviceException)
            }
        })
        return cloudPathName
    }

    fun post(url:String,headers:Map<String,String>?,body:String,callback: Callback){
        val requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),body)
        val requestBuilder = Request.Builder()
            .addHeader("content-type", "application/json")
            .post(requestBody)
            .url(url)
        if(headers?.isNotEmpty() == true){
            headers.forEach {entry ->
                requestBuilder.header(entry.key,entry.value)
            }
        }
        val request = requestBuilder.build()
        mOkHttpClient.newCall(request).enqueue(callback)
    }
}